Skip to content

Fix memory corruption and support WinPE#841

Open
eransha-salvador wants to merge 7 commits intoPowerShell:latestw_allfrom
eransha-salvador:839-fix-memory-corruption-and-support-winpe
Open

Fix memory corruption and support WinPE#841
eransha-salvador wants to merge 7 commits intoPowerShell:latestw_allfrom
eransha-salvador:839-fix-memory-corruption-and-support-winpe

Conversation

@eransha-salvador
Copy link
Copy Markdown

Summary

  • Fix format-string mismatch in load_user_profile (%s %S %d with two args) that corrupts memory and crashes sshd-session post-auth when LoadUserProfileW fails.
  • Delay-load user32.dll in sshd-auth so it survives restricted window stations (e.g. WinPE) where user32's DllMain fails with STATUS_DLL_INIT_FAILED. The only transitive user32 references (ShowWindow / GetWindowPlacement via ConRestoreViewRect_NoPtyHack) are unreachable in sshd-auth, so the DLL is never loaded in practice.

Stacked on #840 — please merge that first. Until #840 lands, this PR's diff view also shows its commit; after #840 merges, this PR will rebase down to just the two commits above.

Fixes PowerShell/Win32-OpenSSH#2435

Test plan

  • Boot WinPE, run sshd, and verify the daemon stays up across an authentication attempt (no sshd-session / sshd-auth crash).
  • On regular Windows, confirm no behavior change for normal profile-enabled logons.

- Prefer VS 2017/2019/2022 over newer installs (e.g. VS 2026) when
  vswhere reports both, so a host with a newer VS installed
  alongside VS 2022 still builds with v143. Without this the
  downstream Select-String version checks miss and the VS 2015
  fallback dereferences a null VS140COMNTOOLS.
- Pin vcpkg's CMake with VCPKG_VISUAL_STUDIO_PATH and
  VCPKG_PLATFORM_TOOLSET=v143 so it uses the same toolset as the
  .vcxproj files, avoiding MSB8040 (Spectre-libs missing for v145)
  when vcpkg would otherwise pick up a newer VS.
- Fall back to the repo root for OpenSSH-build.ps1 -destination when
  \$env:WORKSPACE is unset. CI still has its WORKSPACE value, and no
  longer invokes this script directly anyway.
- Document Windows build prerequisites in README.txt: VS 2022
  Desktop C++ workload, v143 Spectre-mitigated libs, Git, the
  one-time vcpkg bootstrap/integrate step, and the need to run from
  an elevated PowerShell.
@tgauth
Copy link
Copy Markdown
Collaborator

tgauth commented Apr 20, 2026

If it's only a transitive reference, does removing the dependency on the DLL from sshd-auth, instead of delay loading, work?

@eransha-salvador
Copy link
Copy Markdown
Author

@tgauth it won't work.
The use of the user32 lib done in a static library (posix_compat) - since it is static the link is done only in the containing app project (ssh_auth).
If we remove the user32.lib entirely (including from the default additional dependencies) - the link stage will fail to find ShowWindow and GetWindowPlacement.
I used dumpbin /dependents bin\x64\Release\sshd-auth.exe to verify that the user32 is delayed init when adding it explicitly as I did in this pr.
If I will remove it entirely (including from the default additional dependencies) - the link fails.
If I remove it explicitly but keep it in the default additional dependencies - the link pass but user32 will no longer be delayed - which means it will try to load it statically, which in the PE scenario on the launched user fail (due to the original issue that the user it is being launched for has no permission for the resource).

If it's only a transitive reference, does removing the dependency on the DLL from sshd-auth, instead of delay loading, work?

The .vcxproj files pin PlatformToolset v143, which ships with VS 2022.
VS 2017 (v141) and VS 2019 (v142) would need v143 build tools sideloaded
to build this, which is a non-default setup — so they shouldn't be
treated as equals to VS 2022 in the preference order.
@eransha-salvador eransha-salvador force-pushed the 839-fix-memory-corruption-and-support-winpe branch from 2887388 to f98e834 Compare April 21, 2026 05:59
The debug3 on LoadUserProfileW failure had format "%s %S %d" (three
specifiers) but only two arguments. %S consumed GetLastError()'s DWORD
as a wide-string pointer and wcsnlen dereferenced it, crashing
sshd-session post-auth.

Only visible where LoadUserProfileW actually fails (e.g. WinPE, which
has no user-profile service), so regular Windows was unaffected.
sshd-auth runs as a privsep helper under a different user with no
desktop/window-station access. user32's DllMain binds to the process
window station and fails with STATUS_DLL_INIT_FAILED in restricted
environments like WinPE, crashing the helper before auth can run.

Add delayimp.lib and /DELAYLOAD:user32.dll so user32 is only loaded
when one of its APIs is actually called. sshd-auth's only transitive
user32 references come from console.c's ConRestoreViewRect_NoPtyHack
(ShowWindow / GetWindowPlacement), which sshd-auth never executes,
so in practice user32 is never loaded at all.

The ItemDefinitionGroup carrying /DELAYLOAD is placed after
Microsoft.Cpp.props, where the Link item type becomes defined.
@eransha-salvador eransha-salvador force-pushed the 839-fix-memory-corruption-and-support-winpe branch from f98e834 to a650ee0 Compare April 21, 2026 06:03
@tgauth
Copy link
Copy Markdown
Collaborator

tgauth commented Apr 21, 2026

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@tgauth
Copy link
Copy Markdown
Collaborator

tgauth commented Apr 27, 2026

Boot WinPE, run sshd, and verify the daemon stays up across an authentication attempt (no sshd-session / sshd-auth crash).

@eransha-salvador, are you creating a local user account to attempt authentication with?

@eransha-salvador
Copy link
Copy Markdown
Author

eransha-salvador commented Apr 27, 2026

Boot WinPE, run sshd, and verify the daemon stays up across an authentication attempt (no sshd-session / sshd-auth crash).

@eransha-salvador, are you creating a local user account to attempt authentication with?

@tgauth I did - on winpe I'm creating a local user account to login with.
Please note that this branch won't work with public key - it works with passwords only.
I have identified an existing issue (in latestw_all) with public key authentication in winpe, to which I will open a new issue and PR for the fix.

KERB_S4U_LOGON requires a KDC, so it cannot succeed on a workgroup
machine. Prefixed names ("DOMAIN\user") were being routed to the
Kerberos package regardless of host join state — failing on WinPE
(whose SAM hive retains a "minwinpc" domain that diverges from the
runtime computer name) and on any non-joined host where a client
happens to send a qualified username.

Gate Kerberos selection on NetGetJoinInformation. When the host is
not domain-joined, route through MSV1_0 and skip past any leading
backslash so the bare SAM name reaches LsaLogonUser. The auth
package and message struct are now selected by the same predicate,
so they cannot disagree.

Also add a debug3 in get_passwd surfacing the domain/computer name
pair, to make future SAM-vs-GetComputerName divergences easier to
diagnose.
Copilot AI review requested due to automatic review settings April 29, 2026 12:53
@eransha-salvador
Copy link
Copy Markdown
Author

Boot WinPE, run sshd, and verify the daemon stays up across an authentication attempt (no sshd-session / sshd-auth crash).

@eransha-salvador, are you creating a local user account to attempt authentication with?

@tgauth I did - on winpe I'm creating a local user account to login with. Please note that this branch won't work with public key - it works with passwords only. I have identified an existing issue (in latestw_all) with public key authentication in winpe, to which I will open a new issue and PR for the fix.

@tgauth - the last commit handles the issue with public key authentication.
In order to reduce the possible effect - I limited it to the generate_s4u_user_token function. Though I think that the root cause (prefixing with returned domain or a non-domain attached machine) should be done in pwd.c's get_passwd. In winpe even though the windows is not domain-joined, the get_passwd got a domain from the call to LookupAccountSidW - and the domain didn't match the computer name.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a Windows-specific crash/memory corruption in profile-loading error logging and improves WinPE/restricted-session compatibility by preventing sshd-auth from eagerly loading user32.dll. Also updates the Windows build helper/scripts and build documentation to better align with the v143/VS2022 toolchain and vcpkg manifest behavior.

Changes:

  • Fixes a format-string argument mismatch in load_user_profile() and adjusts S4U auth-package selection to use Kerberos only when the machine is domain-joined.
  • Updates sshd-auth.vcxproj to delay-load user32.dll (and link delayimp.lib) to avoid STATUS_DLL_INIT_FAILED in restricted window stations (e.g., WinPE).
  • Updates build docs and build helper logic to prefer VS 2022/v143 and set vcpkg toolset-related environment variables.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
contrib/win32/win32compat/win32_usertoken_utils.c Adds domain-join detection to gate Kerberos usage; fixes LoadUserProfileW debug logging arg mismatch.
contrib/win32/openssh/sshd-auth.vcxproj Adds delayimp.lib and /DELAYLOAD:user32.dll to avoid loading user32 in restricted environments.
contrib/win32/openssh/README.txt Adds build prerequisites and guidance for VS2022/vcpkg/administrator requirements.
contrib/win32/openssh/OpenSSHBuildHelper.psm1 Prefers VS 2022 and pins vcpkg environment to v143/selected VS installation.
contrib/win32/openssh/OpenSSH-build.ps1 Makes $destination default robust when WORKSPACE isn’t set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread contrib/win32/openssh/sshd-auth.vcxproj Outdated
Comment thread contrib/win32/openssh/README.txt
Comment thread contrib/win32/openssh/README.txt
Comment thread contrib/win32/win32compat/win32_usertoken_utils.c Outdated
- Make is_domain_joined_machine static (TU-local) and reformat to
  match the file's brace/indent conventions.
- Remove the README claim that the build script adds Git's cmd
  directory to the machine PATH; no such logic exists in the build
  scripts.
Per PR review: replacing $(AdditionalDependentLibs) with a
hand-written list made sshd-auth's link inputs drift from the rest
of the solution (and dropped libs supplied by paths.targets).
Append delayimp.lib to the inherited value instead. user32.lib is
already in the inherited list; the /DELAYLOAD:user32.dll directive
is unchanged, so delay-load behavior is preserved.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix memory corruption and support WinPE

3 participants